﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.IO.Ports;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace Collector
{
    public partial class Form1 : Form
    {
        #region**参数设置**
        private static SerialPort sp = null;
        private static Boolean isSetProperty = false;
        private static String[] rates = { "2400", "4800", "9600", "19200", "38400", "43000", "56000", "57600", "115200" };
        private static String[] paritys = { "None", "奇校验", "偶校验" };
        private static String[] dataBits = { "5", "6", "7", "8" };
        private static String[] stopBits = { "0", "1", "1.5", "2" };
        #endregion
        #region**初始化**
        public Form1()
        {
            InitializeComponent();
        }

        private void Form1_Load(object sender, EventArgs e)
        {
            // 加载所有
            //Com_zhx.Profile.SaveProfile();
            //Com_zhx.Profile.LoadProfile();
            Profile.LoadProfile();
            DTR.Enabled = false;
            RTS.Enabled = false;
            //加载可用串口，波特率，校验位，数据位，停止位
            SearchPort();
            Load_Rate();
            Load_Parity();
            Load_DataBits();
            Load_StopBits();
            Auto_SenT_Initialize();
        }
        #endregion

        #region**搜索串口按钮**
        private void Search_ComPort_Click(object sender, EventArgs e)
        {
            SearchPort();
        }
        #endregion
        #region**开关串口**
        private void Open_Port_Click(object sender, EventArgs e)
        {
            if (RS485.Checked)
            {
                Open_Port_485();
            }
            else
            {
                Open_Port_Simple();
            }
            Profile.SaveProfile();
        }
        public void Open_Port_485()
        {
            if (Open_Port.Text == "打开串口")
            {
                //Console.WriteLine(CheckPortSetting());

                if (!CheckPortSetting())
                {
                    MessageBox.Show("串口未设置！", "错误提示");
                    return;
                }
                if (!isSetProperty)
                {
                    SetPortProperty();
                }
                try
                {
                    sp.Open();
                    // ColorTranslator.FromHtml("Red");
                    Open_Port.Text = "关闭串口";
                    Open_Port.BackColor = Color.GreenYellow;
                    sp.ReadTimeout = 2000;
                    sp.WriteTimeout = 2000;
                    //串口打开后相关设置不可用
                    Enable_Para(false);
                }
                catch (Exception)
                {
                    //打开串口失败，相应标志位取消
                    isSetProperty = false;
                    Open_Port.Text = "打开串口";
                    MessageBox.Show("串口无效或已被占用！", "错误提示");
                    return;
                }
            }
            else
            {
                try
                {
                    sp.Close();
                    isSetProperty = false;
                    Open_Port.Text = "打开串口";
                    Open_Port.BackColor = SystemColors.Control;
                    Enable_Para(true);
                }
                catch (Exception)
                {
                    MessageBox.Show("关闭串口时发生错误");
                }

            }
        }
        public void Open_Port_Simple()
        {
            if (Open_Port.Text == "打开串口")
            {
                //Console.WriteLine(CheckPortSetting());
                if (!CheckPortSetting())
                {
                    MessageBox.Show("串口未设置！", "错误提示");
                    return;
                }
                if (!isSetProperty)
                {
                    SetPortProperty();
                }
                try
                {
                    sp.Open();
                    Open_Port.Text = "关闭串口";
                    Open_Port.BackColor = Color.DarkSeaGreen;
                    //串口打开后相关设置不可用
                    Enable_Para(false);
                }
                catch (Exception)
                {
                    //打开串口失败，相应标志位取消
                    isSetProperty = false;
                    Open_Port.Text = "打开串口";
                    MessageBox.Show("串口无效或已被占用！", "错误提示");
                    return;
                }
            }
            else
            {
                try
                {
                    sp.Close();
                    isSetProperty = false;
                    Open_Port.Text = "打开串口";
                    Open_Port.BackColor = SystemColors.Control;
                    Enable_Para(true);
                }
                catch (Exception)
                {
                    MessageBox.Show("关闭串口时发生错误");
                }

            }
        }
        #endregion
        #region**启停RS485**
        private void RS485_CheckedChanged(object sender, EventArgs e)
        {
            if (RS485.Checked)
            {
                DTR.Checked = RTS.Checked = true;
            }
            else
            {
                DTR.Checked = RTS.Checked = false;
            }
        }
        #endregion
        #region**使能参数设置**
        public void Enable_Para(Boolean ifEnable)
        {
            Cbx_ComPort.Enabled = ifEnable;
            Cbx_BaudRate.Enabled = ifEnable;
            Cbx_DataBits.Enabled = ifEnable;
            Cbx_Parity.Enabled = ifEnable;
            Cbx_StopBits.Enabled = ifEnable;
            Hex_Rec.Enabled = ifEnable;
            RS485.Enabled = ifEnable;

        }
        #endregion


        #region**加载可用串口**
        //****************************加载可用串口****************************************************************
        public void SearchPort()
        {
            bool comExistence = false;//有可用串口标志位
            Cbx_ComPort.Items.Clear(); //清除当前串口号中的所有串口名称
            var coms = SerialPort.GetPortNames();
            foreach (var item in coms)
            {
                Cbx_ComPort.Items.Add(item);
                comExistence = true;
            }
            if (comExistence)
            {
                Cbx_ComPort.SelectedIndex = 0;//使 comBox 显示第 1 个添加的索引
            }
            else
            {
                MessageBox.Show("没有找到可用串口！", "错误提示");
            }
        }
        #endregion
        #region**加载波特率**
        public void Load_Rate()
        {
            foreach (String rate in rates)
            {
                Cbx_BaudRate.Items.Add(rate);
            }
            // 预置波特率
            switch (Profile.G_BAUDRATE)
            {
                case "2400":
                    Cbx_BaudRate.SelectedIndex = 0;
                    break;
                case "4800":
                    Cbx_BaudRate.SelectedIndex = 1;
                    break;
                case "9600":
                    Cbx_BaudRate.SelectedIndex = 2;
                    break;
                case "19200":
                    Cbx_BaudRate.SelectedIndex = 3;
                    break;
                case "38400":
                    Cbx_BaudRate.SelectedIndex = 4;
                    break;
                case "43000":
                    Cbx_BaudRate.SelectedIndex = 5;
                    break;
                case "56000":
                    Cbx_BaudRate.SelectedIndex = 6;
                    break;
                case "57600":
                    Cbx_BaudRate.SelectedIndex = 7;
                    break;
                case "115200":
                    Cbx_BaudRate.SelectedIndex = 8;
                    break;
                default:
                    {
                        MessageBox.Show("波特率预置参数错误。");
                        return;
                    }
            }
        }
        #endregion
        #region**加载校验位**
        public void Load_Parity()
        {
            foreach (String parity in paritys)
            {
                Cbx_Parity.Items.Add(parity);
            }
            switch (Profile.G_PARITY)
            {
                case "None":
                    Cbx_Parity.SelectedIndex = 0;
                    break;
                case "ODD":
                    Cbx_Parity.SelectedIndex = 1;
                    break;
                case "EVEN":
                    Cbx_Parity.SelectedIndex = 2;
                    break;
                default:
                    {
                        MessageBox.Show("停止位预置参数错误。");
                        return;
                    }
            }
        }
        #endregion
        #region**加载数据位**
        public void Load_DataBits()
        {
            foreach (String dataBit in dataBits)
            {
                Cbx_DataBits.Items.Add(dataBit);
            }
            switch (Profile.G_DATABITS)
            {
                case "5":
                    Cbx_DataBits.SelectedIndex = 0;
                    break;
                case "6":
                    Cbx_DataBits.SelectedIndex = 1;
                    break;
                case "7":
                    Cbx_DataBits.SelectedIndex = 2;
                    break;
                case "8":
                    Cbx_DataBits.SelectedIndex = 3;
                    break;
                default:
                    {
                        MessageBox.Show("数据位预置参数错误。");
                        return;
                    }
            }
        }
        #endregion
        #region**加载停止位**
        public void Load_StopBits()
        {
            foreach (String stopbit in stopBits)
            {
                Cbx_StopBits.Items.Add(stopbit);
            }
            switch (Profile.G_STOP)
            {
                case "0":
                    Cbx_StopBits.SelectedIndex = 0;
                    break;
                case "1":
                    Cbx_StopBits.SelectedIndex = 1;
                    break;
                case "1.5":
                    Cbx_StopBits.SelectedIndex = 2;
                    break;
                case "2":
                    Cbx_StopBits.SelectedIndex = 3;
                    break;
                default:
                    {
                        MessageBox.Show("停止位预置参数错误。");
                        return;
                    }
            }
        }

        #endregion
        #region**参数设置检查**
        private bool CheckPortSetting()
        {   /*
            Console.WriteLine(CbxCOMPort.Text.Trim());
            Console.WriteLine(CbxBaudRate.Text.Trim());
            Console.WriteLine(CbxDataBits.Text.Trim());
            Console.WriteLine(CbxParity.Text.Trim());
            Console.WriteLine(CbxStopBits.Text.Trim());
            */
            if (Cbx_ComPort.Text.Trim() == "") return false;
            if (Cbx_BaudRate.Text.Trim() == "") return false;
            if (Cbx_DataBits.Text.Trim() == "") return false;
            if (Cbx_Parity.Text.Trim() == "") return false;
            if (Cbx_StopBits.Text.Trim() == "") return false;
            return true;
        }
        #endregion
        #region**设置串口**
        private void SetPortProperty()//设置串口
        {
            sp = new SerialPort
            {
                //串口号
                PortName = Cbx_ComPort.Text.Trim(),
                //波特率
                BaudRate = Convert.ToInt32(Cbx_BaudRate.Text.Trim())
            };
            //停止位
            float f = Convert.ToSingle(Cbx_StopBits.Text.Trim());
            if (f == 0)
            {
                sp.StopBits = StopBits.None;
            }
            else if (f == 1.5)
            {
                sp.StopBits = StopBits.OnePointFive;
            }
            else if (f == 1)
            {
                sp.StopBits = StopBits.One;
            }
            else if (f == 2)
            {
                sp.StopBits = StopBits.Two;
            }
            //数据位
            sp.DataBits = Convert.ToInt16(Cbx_DataBits.Text.Trim());
            //校验位
            string s = Cbx_Parity.Text.Trim();
            if (s.CompareTo("None") == 0)
            {
                sp.Parity = Parity.None;
            }
            else if (s.CompareTo("奇校验") == 0)
            {
                sp.Parity = Parity.Odd;
            }
            if (s.CompareTo("偶校验") == 0)
            {
                sp.Parity = Parity.Even;
            }
            else
            {
                sp.Parity = Parity.None;
            }

            //****************设置DTR、RTS有效******************
            if (RS485.Checked)
            {
                sp.DtrEnable = true;
                sp.RtsEnable = true;
            }
            else
            {
                sp.DtrEnable = false;
                sp.RtsEnable = false;
            }
            //超时读取
            sp.ReadTimeout = 2000;
            sp.DataReceived += new SerialDataReceivedEventHandler(sp_DataReceived);
            //串口配置完成
            isSetProperty = true;
        }
        #endregion

        #region**定时发送**
        private void Auto_Sen_CheckedChanged(object sender, EventArgs e)
        {
            if (Auto_Sen.Checked)
            {
                //设置Timer控件可用
                this.timer1.Enabled = true;
                //设置时间间隔（毫秒为单位）
                try
                {
                    this.timer1.Interval = int.Parse(Auto_SenT.Text);
                }
                catch (Exception)
                {
                    this.timer1.Interval = 1000;
                }


            }
            else
            {
                //设置Timer控件可用
                this.timer1.Enabled = false;
            }
        }
        private void timer1_Tick(object sender, EventArgs e)
        {
            Send.PerformClick();
        }
        #endregion
        #region**发送数据**
        private void Send_Click(object sender, EventArgs e)
        {
            if (Open_Port.Text == "关闭串口")//写串口数据
            {
                if (TextBox_Sen.Text == "" || TextBox_Sen.Text == null)//检测要发送的数据
                {
                    MessageBox.Show("请输入要发送的数据！", "错误提示");
                    Auto_Sen.Checked = false;
                    return;
                }
                else
                {
                    if (RS485.Checked)
                    {
                        Send_485();
                    }
                    else
                    {
                        Send_Simple();
                    }
                }
            }
            else
            {
                MessageBox.Show("串口未打开！", "错误提示");
                Auto_Sen.Checked = false;
                return;
            }
        }
        public void Send_485()
        {
            byte[] sendbuf = new byte[TextBox_Sen.Text.Length / 2]; ;//tbxSendData.Text.ToCharArray();
            for (int i = 0; i < sendbuf.Length; i++)
            {
                try
                {
                    // 每两个字符是一个 byte。 
                    sendbuf[i] = byte.Parse(TextBox_Sen.Text.Substring(i * 2, 2),
                    System.Globalization.NumberStyles.HexNumber);
                }
                catch
                {
                    // Rethrow an exception with custom message. 
                    throw new ArgumentException("hex is not a valid hex number!", "hex");
                }
            }

            //Console.WriteLine(TbxSendData.Text.Length);
            //Console.WriteLine(sendbuf.Length);
            /*for(int j=0;j< sendbuf.Length; j++)
            {
                Console.WriteLine(sendbuf[j]);
            }
            */
            SendModbusData(ref sendbuf);
        }
        public void Send_Simple()
        {
            try
            {
                sp.WriteLine(TextBox_Sen.Text);
            }
            catch (Exception)
            {
                MessageBox.Show("发送数据时发生错误！", "错误提示");
                return;
            }
        }




        #endregion
        #region**接收数据**
        public void sp_DataReceived(object sender, SerialDataReceivedEventArgs e)
        {

            System.Threading.Thread.Sleep(100);//延时100ms完全接收数据
            //this.Invoke跨线程访问，辅助线程访问主线程控件
            this.Invoke((EventHandler)(delegate
            {
                if (RS485.Checked)
                {
                    DataReceived_485();
                }
                else
                {
                    DataReceived_Simple();
                }
            }));
        }

        public void DataReceived_485()
        {
            //Console.WriteLine("kkk");
            DateTime dt = DateTime.Now;
            if (sp.IsOpen)
            {
                try
                {
                    //string str = readBuffer.ToString();
                    int count = sp.BytesToRead;//读取缓冲区数据字节（8位）数
                    if (count > 0)
                    {
                        Byte[] readBuffer = new Byte[sp.BytesToRead];//创建接收字节数组
                        sp.Read(readBuffer, 0, readBuffer.Length);//读取接收的数据
                        String RecvDataText = null;
                        String RescDataCRC = null;

                        TextBox_Rec.Text += dt.GetDateTimeFormats('f')[0].ToString() + "\r\n";
                        //GetResponse(ref readBuffer);//readBuffer[0]一个字节                   
                        // CRC 验证
                        if (CheckResponse(readBuffer))
                        {
                            //显示输入数据
                            for (int i = 0; i < readBuffer.Length; i++)
                            {
                                RecvDataText += ("0x" + readBuffer[i].ToString("X2") + " ");

                                //RecvDataText += (ReceivedData[i].ToString("X2") + " ");
                            }
                            byte[] CRC = new byte[2];
                            GetCRC(readBuffer, ref CRC);
                            //未进行CRC校验
                            for (int j = 0; j < 2; j++)
                            {
                                RescDataCRC += ("0x" + CRC[j].ToString("X2") + " ");
                            }
                            TextBox_Rec.Text += RecvDataText + "\r\n";
                            sp.DiscardInBuffer();//清除串行驱动程序的接收缓冲区的数据；  
                        }
                        else
                        {
                            //modbusStatus = "CRC校验 error";
                            sp.DiscardInBuffer();
                        }
                    }
                    else
                    {
                        //**************************************************************************************************
                        TextBox_Rec.Text += "读取缓冲区数据失败，请重启系统" + "\r\n";
                    }
                }
                catch (Exception)
                {
                    sp.DiscardInBuffer();//读取缓冲区数据字节（8位）数   失败                           
                }


            }
            else
            {
                // modbusStatus = "Serial port not open";                    
            }
            //Console.WriteLine(weightdate);
        }
        public void DataReceived_Simple()
        {
            DateTime dt = DateTime.Now;
            if (!Hex_Rec.Checked)
            {
                TextBox_Rec.Text += dt.GetDateTimeFormats('f')[0].ToString() + "\r\n";
                //TextBox_Rec.Text += sp.Read() + "\r\n";
                /*
                 * sp.ReadLine()是线程阻塞的，机器向串口发送数据然后 串口有数据接收时，sp.ReadLine()才会继续向下执行。
                 * 因为下位机没有发回车换行来，默认是\n结束，相当于执行ReadTo('\n')因为下位机没发\n，所以一直卡在这里等待。
                */
                //TextBox_Rec.Text += sp.ReadLine() + "\r\n";
                try
                {
                    Byte[] receivedData = new Byte[sp.BytesToRead];        //创建接收字节数组
                    sp.Read(receivedData, 0, receivedData.Length);         //读取数据
                    TextBox_Rec.Text += new UTF8Encoding().GetString(receivedData) + "\r\n";//用万能的UTF8可以传输中文不会乱码
                }
                catch (System.Exception ex)
                {
                    MessageBox.Show(ex.Message, ":出错提示!!!!!");
                }
            }
            else
            {
                int i_InNum;//输入缓冲区中字节数
                i_InNum = sp.BytesToRead;

                Byte[] ReceivedData = new Byte[sp.BytesToRead];//创建接收字节数组
                sp.Read(ReceivedData, 0, ReceivedData.Length);//读取接收的数据
                String RecvDataText = null;

                for (int i = 0; i < ReceivedData.Length; i++)
                {
                    RecvDataText += ("0x" + ReceivedData[i].ToString("X2") + " ");
                }
                TextBox_Rec.Text += dt.GetDateTimeFormats('f')[0].ToString() + "\r\n";
                TextBox_Rec.Text += RecvDataText + "\r\n";
            }
            //丢弃接收缓冲区数据
            sp.DiscardInBuffer();
        }

        #endregion
        #region**Check Response**
        private bool CheckResponse(byte[] response)
        {
            //Perform a basic CRC check:
            byte[] CRC = new byte[2];
            GetCRC(response, ref CRC);
            if (CRC[0] == response[response.Length - 2] && CRC[1] == response[response.Length - 1])
                return true;
            else
                return false;
        }
        #endregion
        #region**Get Response**
        private void GetResponse(ref byte[] response)
        {
            //There is a bug in .Net 2.0 DataReceived Event that prevents people from using this
            //event as an interrupt to handle data (it doesn't fire all of the time).  Therefore
            //we have to use the ReadByte command for a fixed length as it's been shown to be reliable.
            for (int i = 0; i < response.Length; i++)
            {
                response[i] = (byte)(sp.ReadByte());//从输入缓冲区同步读取一个字节
            }
            return;
        }
        #endregion
        #region**CRC Computation**
        public void GetCRC(byte[] message, ref byte[] CRC)
        {
            //Function expects a modbus message of any length as well as a 2 byte CRC array in which to 
            //return the CRC values:

            ushort CRCFull = 0xFFFF;
            byte CRCHigh = 0xFF, CRCLow = 0xFF;
            char CRCLSB;

            for (int i = 0; i < (message.Length) - 2; i++)
            {
                CRCFull = (ushort)(CRCFull ^ message[i]);

                for (int j = 0; j < 8; j++)
                {
                    CRCLSB = (char)(CRCFull & 0x0001);
                    CRCFull = (ushort)((CRCFull >> 1) & 0x7FFF);

                    if (CRCLSB == 1)
                        CRCFull = (ushort)(CRCFull ^ 0xA001);
                }
            }
            CRC[1] = CRCHigh = (byte)((CRCFull >> 8) & 0xFF);
            CRC[0] = CRCLow = (byte)(CRCFull & 0xFF);
        }
        #endregion
        #region**SendModbusData 打包发送数据**
        public bool SendModbusData(ref byte[] values)
        {
            //Ensure port is open:
            if (sp.IsOpen)
            {
                //Clear in/out buffers:
                sp.DiscardOutBuffer();//清空发送、接收缓冲区字节
                sp.DiscardInBuffer();
                //Function 3 response buffer:
                byte[] response = new byte[values.Length + 2];
                Array.Copy(values, response, values.Length);
                //BuildMessage(address, (byte)3, start, registers, ref message);
                //打包带有 CRC 验证的modbus 数据包:
                byte[] CRC = new byte[2];
                GetCRC(response, ref CRC);
                //Console.WriteLine(CRC[0]);
                //Console.WriteLine(CRC[1]);
                response[0] = Convert.ToByte(response[0]);//地址
                response[1] = Convert.ToByte(response[1]);//功能
                //values[2] = (byte)(Convert.ToByte(values[2])>>8);//寄存器地址
                response[2] = Convert.ToByte(response[2]);
                response[3] = Convert.ToByte(response[3]);
                //values[2] = (byte)(Convert.ToByte(values[]) >> 8);//寄存器个数
                response[4] = Convert.ToByte(response[4]);
                response[5] = Convert.ToByte(response[5]);

                response[response.Length - 2] = CRC[0];
                response[response.Length - 1] = CRC[1];
                values = response; //返回带有 CRC 验证的modbus 数据包
                /*
                Console.WriteLine(values.Length); 
                for (int i=0;i<values.Length;i++){
                     Console.WriteLine(values[i]);//控制台输出打包后的Modbus数据
                }
                */
                //Send modbus message to Serial Port:
                try
                {
                    sp.WriteTimeout = 2000;
                    sp.Write(values, 0, values.Length);

                    //GetResponse(ref response);
                    return true;
                }
                catch (Exception ex)
                {
                    MessageBox.Show(ex.Message, "错误提示", MessageBoxButtons.OK, MessageBoxIcon.Error);
                    return false;
                }
                //Evaluate message:
                //if (CheckResponse(response))
                //{
                //    //Return requested register values:
                //    for (int i = 0; i < (response.Length - 5) / 2; i++)
                //    {
                //        values[i] = response[2 * i + 3];
                //        values[i] <<= 8;
                //        values[i] += response[2 * i + 4];
                //    }
                //    modbusStatus = "Read successful";
                //    return true;
                //}
                //else
                //{
                //    modbusStatus = "CRC error";
                //    return false;
                //}
            }
            else
            {
                MessageBox.Show("串口未打开！");
                return false;
            }

        }
        #endregion


        #region**保存配置**
        private void Save_Cfg_Click(object sender, EventArgs e)
        {
            Profile.G_BAUDRATE = Cbx_BaudRate.Text;
            Profile.G_DATABITS = Cbx_DataBits.Text;
            Profile.G_STOP = Cbx_StopBits.Text;
            Profile.G_PARITY = Cbx_Parity.Text;
            //Com_zhx.Profile.SaveProfile();
            Profile.SaveProfile();
        }
        #endregion
        #region**周期样式**
        private void Auto_SenT_Enter(object sender, EventArgs e)
        {
            Auto_SenT.Text = "";
            Auto_SenT.ForeColor = Color.Black;
        }
        private void Auto_SenT_Leave(object sender, EventArgs e)
        {
            try
            {
                int b = int.Parse(Auto_SenT.Text);
            }
            catch (Exception)
            {
                Auto_SenT_Initialize();
            }
        }
        public void Auto_SenT_Initialize()
        {
            Auto_SenT.Text = "周期：1000ms";
            Auto_SenT.ForeColor = Color.LightGray;
        }
        #endregion
        #region**延时**
        public bool Wait(int ms)
        {
            DateTime t = DateTime.Now;
            for (int i = 0; i < ms; i++)
            {
                TimeSpan ts = DateTime.Now - t;
                if (ts.TotalSeconds >= ms)
                {
                    Console.WriteLine(ts.TotalMilliseconds);
                    i = ms + 2;
                }
                i--;
            }
            return true;
        }

        #endregion
        #region**清空接收发送区**
        private void Clear_Sen_Click(object sender, EventArgs e)
        {
            TextBox_Sen.Text = "";
        }

        private void Clear_Recive_Click(object sender, EventArgs e)
        {
            TextBox_Rec.Text = "";
        }
        #endregion
        #region**自动清空接收区**
        private void AutoClear_CheckedChanged(object sender, EventArgs e)
        {
            if (AutoClear.Checked)
            {
                //设置Timer控件可用
                this.timer2.Enabled = true;
                //设置时间间隔（毫秒为单位）
                this.timer2.Interval = 3000;
            }
            else
            {
                this.timer2.Enabled = false;
            }
        }
        private void timer2_Tick(object sender, EventArgs e)
        {
            this.Invoke(new EventHandler(delegate
            {
                TextBox_Rec.Text = "";
            }));
        }
        #endregion
    }
}
